package br.com.sgac.dao;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;

public class GenericDAO<T> {
	
		protected static EntityManager manager;
		
		protected GenericDAO(){
			manager = getManager();
		}
		
		protected static EntityManager getManager(){
			if(manager == null || !manager.isOpen()){
				EntityManagerFactory factory = Persistence.createEntityManagerFactory("sgac");
				manager  = factory.createEntityManager();
				manager.setFlushMode(FlushModeType.COMMIT);
			}
			return manager;
		}
		
		public void begin(){
			if(!getManager().getTransaction().isActive())
				getManager().getTransaction().begin();
		}
		
		public void close(){
			if(getManager().isOpen())
				getManager().close();
		}
		
		public void persist(T obj){
			getManager().persist(obj);
		}
		
		public void remove(T obj){
			getManager().remove(obj);
		}
		
		public T merge(T obj){
			return getManager().merge(obj);
		}
		
		public void refresh(T obj){
			getManager().refresh(obj);
		}
		
		public void flush(){
			getManager().flush();
		}
		
		public void clear(){
			getManager().clear();
		}
		
		//Remove a entidade do contexto de persistencia, tornado-o detached(desacoplado)
		public void detach(T obj){
			getManager().detach(obj);
		}
		
		public void commit(){
			if(getManager().getTransaction().isActive())
				getManager().getTransaction().commit();
		}
		
		public void rollback(){
			if(getManager().getTransaction().isActive())
				getManager().getTransaction().rollback();
		}
		
		@SuppressWarnings("unchecked")
		public T find(Object chave){
			Class<T> type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
			return getManager().find(type, chave);
		}
		
		@SuppressWarnings("unchecked")
		public List<T> findAll(){
			Class<T> type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
			Query query = getManager().createQuery("select x from " + type.getSimpleName() + " x");
			return (List<T>) query.getResultList();
		}
		@SuppressWarnings("unchecked")
		public Object findByID(String id){
			Class<T> type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
			Query query = getManager().createQuery("select x from " + type.getSimpleName() + " x where x.id = " + id);
			return query.getSingleResult();
		}
		//---------------------******-----------------//
		
		public Object findByQuery(String consulta){
			try{
				Query query = getManager().createQuery(consulta);
				return query.getSingleResult();
			}
			catch(NoResultException e){
				return null;
			}
			catch(NonUniqueResultException e){
				return null;
			}
		}
		
		@SuppressWarnings("rawtypes")
		public List findAllByQuery(String consulta){
			try{
				Query query = getManager().createQuery(consulta);
				return query.getResultList();
			}
			catch(NoResultException e){
				return null;
			}
			catch(NonUniqueResultException e){
				return null;
			}
		}
		
		public Object findByQueryNativa(String consulta){
			Query query = getManager().createQuery(consulta);
			return query.getSingleResult();
		}

		public boolean executarQueryUpdate(String consulta){
			try {
			manager.getTransaction().begin();
			Query query = getManager().createQuery(consulta);
			query.executeUpdate();
			manager.getTransaction().commit();
			return true;
			}
			catch(Error e){
				return false;
			}
		}
		
		@SuppressWarnings("rawtypes")
		public List findAllByQueryNativa(String consulta){
			Query query = getManager().createQuery(consulta);
			return query.getResultList();
		}
	}
